/*
 *  Copyright 2009 Rohan Ranade.
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *  under the License.
 */
package org.netbeans.modules.cpextender.api;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import org.netbeans.api.java.classpath.ClassPath;
import org.netbeans.api.java.project.JavaProjectConstants;
import org.netbeans.api.java.project.classpath.ProjectClassPathModifier;
import org.netbeans.api.project.Project;
import org.netbeans.api.project.ProjectManager;
import org.netbeans.api.project.ProjectUtils;
import org.netbeans.api.project.SourceGroup;
import org.netbeans.api.project.Sources;
import org.netbeans.modules.cpextender.util.Utilities;
import org.netbeans.spi.project.AuxiliaryConfiguration;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.FileUtil;
import org.openide.util.Exceptions;
import org.openide.util.Mutex;
import org.openide.xml.XMLUtil;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

/**
 * TODO - extension for a free form project
 * 
 * @author Rohan Ranade
 */
public class CPExtensionManager {
    private final Project project;
    private String filePath = null;

    private static final String CP_EXTENSION_DATA = "cp-extension-data"; // NOI18N
    private static final String CONFIG_FILE = "config-file"; // NOI18N
    private static final String CP_EXTENSION_DATA_NS = "http://www.netbeans.org/ns/cp-extension-data/1"; // NOI18N

    public CPExtensionManager(Project project) {
        this.project = project;
    }
    
    public boolean isExtendedProject() {
        return getExtensionFilePath() != null;
    }

    public String getExtensionFilePath() {
        mutex().readAccess(new Runnable() {
            public void run() {
                readConfiguration();
            }
        });
        return filePath;
    }

    public void setExtensionFilePath(final String filePath) {
        mutex().postWriteRequest(new Runnable() {
            public void run() {
                // path configuration should be done before writing the new config
                configurePaths(filePath);
                writeConfiguration(filePath);
                
            }
        });
    }

    public Mutex mutex() {
        return ProjectManager.mutex();
    }

    private void readConfiguration() {
        assert mutex().isReadAccess();
        AuxiliaryConfiguration auxConfig = Utilities.getAuxiliaryConfiguration(project);
        Element configEl = auxConfig.getConfigurationFragment(CP_EXTENSION_DATA, CP_EXTENSION_DATA_NS, true);
        if (configEl != null) {
            NodeList list = configEl.getElementsByTagNameNS(CP_EXTENSION_DATA_NS, CONFIG_FILE);
            if (list.getLength() > 0) {
                Element configFileEl = (Element)list.item(0);
                filePath = configFileEl.getTextContent();
            }
        }
    }

    private void writeConfiguration(String filePath) {
        assert mutex().isWriteAccess();
        Document doc = XMLUtil.createDocument(CP_EXTENSION_DATA, CP_EXTENSION_DATA_NS, null, null);
        Element configEl = doc.getDocumentElement();
        Element configFileEl = configEl.getOwnerDocument().createElementNS(CP_EXTENSION_DATA_NS, CONFIG_FILE);
        configEl.appendChild(configFileEl);
        configFileEl.appendChild(configFileEl.getOwnerDocument().createTextNode(filePath));
        AuxiliaryConfiguration auxConfig = Utilities.getAuxiliaryConfiguration(project);
        auxConfig.putConfigurationFragment(configEl, true);
    }

    private void configurePaths(String newFilePath) {
        // TODO - remove old paths from old file before adding new ones
        File file = new File(newFilePath);
        BufferedReader reader = null;
        List<URL> paths = new ArrayList<URL>();
        try {
            reader = new BufferedReader(new FileReader(file));
            String line = null;
            while ((line = reader.readLine()) != null) {
                if (!line.endsWith("jar")) {
                    continue;
                }
                line = line.trim();
                File f = new File(line);
                FileObject fo = FileUtil.toFileObject(f);
                if (!FileUtil.isArchiveFile(fo)) {
                    continue;
                }
                FileObject root = FileUtil.getArchiveRoot(fo);
                paths.add(root.getURL());
            }
        } catch (FileNotFoundException ex) {
            Exceptions.printStackTrace(ex);
        } catch (IOException ex) {
            Exceptions.printStackTrace(ex);
        } finally {
            try {
                if (reader != null) {
                    reader.close();
                }
            } catch (IOException ex) {
                Exceptions.printStackTrace(ex);
            }
        }

        if (paths.isEmpty()) {
            return;
        }

        try {
            Sources sources = ProjectUtils.getSources(project);
            assert sources != null;
            SourceGroup[] sourceGroups = sources.getSourceGroups(JavaProjectConstants.SOURCES_TYPE_JAVA);
            // currently considering only the first group
            FileObject srcRoot = sourceGroups[0].getRootFolder();
            ProjectClassPathModifier.addRoots(paths.toArray(new URL[paths.size()]),
                    srcRoot, ClassPath.COMPILE);
        } catch (UnsupportedOperationException ex) {
            Exceptions.printStackTrace(ex);
        } catch (IOException ex) {
            Exceptions.printStackTrace(ex);
        }
    }
}
